package cn.bytepulse.utils;

import cn.bytepulse.config.minio.IMinioProperties;
import cn.bytepulse.exception.QuicklyException;
import io.minio.*;
import io.minio.errors.MinioException;
import io.minio.http.Method;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.tika.Tika;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author jiejiebiezheyang
 * @since 2025-03-16 18:00
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class MinioUtils {

    private final MinioClient minioClient;

    private final IMinioProperties iMinioProperties;

    /**
     * 检查文件是否存在
     *
     * @param objectName 文件名称
     */
    public boolean fileExists(String objectName) {
        try {
            minioClient.statObject(
                    StatObjectArgs.builder()
                            .bucket(iMinioProperties.getBucketName())
                            .object(objectName)
                            .build());
            log.info("MinioUtils 文件存在: {}", objectName);
            return true;
        } catch (Exception e) {
            log.info("MinioUtils 文件不存在: {}", objectName);
            return false;
        }
    }

    /**
     * 检查文件是否存在
     *
     * @param objectName 文件名称
     */
    public boolean directoryExists(String objectName) {
        objectName = objectName.endsWith("/") ? objectName : objectName + "/";
        try {
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .bucket(iMinioProperties.getBucketName())
                            .prefix(objectName)  // 以这个前缀开头的文件
                            .recursive(true) // 递归查询
                            .build()
            );

            // 遍历结果，如果有任何文件，就认为目录存在
            for (Result<Item> result : results) {
                if (result.get() != null) {
                    return true;
                }
            }
            log.info("MinioUtils 目录存在: {}", objectName);
            return true;
        } catch (Exception e) {
            log.info("MinioUtils 目录不存在: {}", objectName);
            return false;
        }
    }

    /**
     * 上传文件
     */
    public void uploadFile(String objectName, File file) throws Exception {
        try (InputStream inputStream = new FileInputStream(file)) {
            Tika tika = new Tika();
            FileInputStream stream = new FileInputStream(file);
            String contentType = tika.detect(stream);
            stream.close();
            Path path = Paths.get(file.getAbsolutePath());
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(iMinioProperties.getBucketName())
                    .object(objectName)
                    .stream(inputStream, file.length(), -1)
                    .contentType(contentType)
                    .build());
            log.info("MinioUtils 文件上传成功: {}", objectName);
        } catch (MinioException e) {
            log.info("MinioUtils 文件上传失败: {}", objectName);
            throw new QuicklyException(e.getMessage());
        }
    }

    /**
     * 删除文件
     *
     * @param objectName 文件对象名
     */
    public void deleteFile(String objectName) throws Exception {
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(iMinioProperties.getBucketName()).object(objectName).build());
            log.info("MinioUtils 文件删除成功: {}", objectName);
        } catch (MinioException e) {
            log.info("MinioUtils 文件删除失败: {}", objectName);
            throw new QuicklyException(e.getMessage());
        }
    }

    /**
     * 删除指定目录及其所有内容
     *
     * @param directoryPath 要删除的目录路径（以/结尾）
     */
    public boolean deleteDirectory(String directoryPath) throws Exception {
        // 确保目录路径以/结尾
        if (!directoryPath.endsWith("/")) {
            directoryPath += "/";
        }

        // 列出目录下所有对象
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder()
                        .bucket(iMinioProperties.getBucketName())
                        .prefix(directoryPath)
                        .recursive(true)
                        .build());

        // 先删除目录下的所有对象
        for (Result<Item> result : results) {
            Item item = result.get();
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(iMinioProperties.getBucketName())
                            .object(item.objectName())
                            .build());
        }

        // 然后删除目录本身（在MinIO中目录是虚拟的，但可以删除空"目录"）
        try {
            // 尝试删除目录标记（如果有）
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(iMinioProperties.getBucketName())
                            .object(directoryPath)
                            .build());
            log.info("MinioUtils 目录删除成功: {}", directoryPath);
            return true;
        } catch (Exception e) {
            log.info("MinioUtils 目录删除失败: {}", directoryPath);
            throw new Exception(e.getMessage());
        }
    }

    /**
     * 获取文件信息
     *
     * @param objectName 文件名称
     */
    public StatObjectResponse fileInfo(String objectName) throws Exception {
        return minioClient.statObject(
                StatObjectArgs.builder()
                        .bucket(iMinioProperties.getBucketName())
                        .object(objectName)
                        .build());
    }

    /**
     * 列出存储桶中的所有文件
     *
     * @param directoryPath 目录路径 (例如: "my-folder/")
     * @param recursive     是否递归子目录
     */
    public List<Item> fileList(String directoryPath, boolean recursive) throws Exception {
        // 确保目录路径以/结尾
        if (!directoryPath.endsWith("/")) {
            directoryPath += "/";
        }
        List<Item> items = new ArrayList<>();
        try {
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder()
                            .bucket(iMinioProperties.getBucketName())
                            .prefix(directoryPath)
                            .recursive(recursive) // 递归子目录
                            .build()
            );

            for (Result<Item> result : results) {
                items.add(result.get());
            }
        } catch (Exception e) {
            throw new QuicklyException(e.getMessage());
        }
        return items;
    }

    /**
     * 列出指定目录下的所有文件名
     *
     * @param directoryPath 目录路径 (例如: "my-folder/")
     * @param recursive     是否递归子目录
     */
    public List<String> fileNameList(String directoryPath, boolean recursive) {
        List<String> fileNames = new ArrayList<>();
        try {
            List<Item> items = fileList(directoryPath, recursive);

            for (Item item : items) {
                String objectName = item.objectName().replace(directoryPath, "");
                objectName = objectName.startsWith("/") ? objectName.substring(1) : objectName;
                fileNames.add(objectName);
            }
        } catch (Exception e) {
            throw new QuicklyException(e.getMessage());
        }
        return fileNames;
    }

    /**
     * 生成临时访问 URL（预签名 URL）
     *
     * @param objectName 文件对象名
     * @param expiry     链接有效时长(秒)
     */
    public String preSignedUrl(String objectName, int expiry) throws Exception {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .bucket(iMinioProperties.getBucketName())
                        .object(objectName)
                        .method(Method.GET)  // 生成 GET 方式的 URL
                        .expiry(expiry, TimeUnit.SECONDS) // 设置 URL 过期时间
                        .build()
        );
    }

    /**
     * 下载文件
     *
     * @param objectName 文件对象名
     */
    public InputStream downloadFile(String objectName) throws Exception {
        try {
            return minioClient.getObject(GetObjectArgs.builder().bucket(iMinioProperties.getBucketName()).object(objectName).build());
        } catch (MinioException e) {
            throw new QuicklyException(e.getMessage());
        }
    }


    /**
     * 返回给前端下载
     *
     * @param objectName 文件对象名
     * @param filename   指定文件名
     */
    public ResponseEntity<InputStreamResource> returnFile(String objectName, String filename) throws Exception {
        InputStream inputStream = downloadFile(objectName);
        BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);

        // 标记流的起始位置
        bufferedInputStream.mark(Integer.MAX_VALUE);

        // 使用 Tika 检测文件类型
        Tika tika = new Tika();
        String contentType = tika.detect(bufferedInputStream);

        // 重置流到起始位置
        bufferedInputStream.reset();

        // 设置响应头
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);
        headers.add(HttpHeaders.CONTENT_TYPE, contentType);

        return ResponseEntity.ok().headers(headers).body(new InputStreamResource(bufferedInputStream));
    }

    /**
     * 返回给前端下载
     * 文件名=文件对象名
     *
     * @param objectName 文件对象名|文件名
     */
    public ResponseEntity<InputStreamResource> returnFile(String objectName) throws Exception {
        return returnFile(objectName, objectName);
    }
}
